package com.miya.demo.support.asyn;

import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * CompletableFuture 静态方法:
 *  1. 执行任务, 第二个参数为线程池, 若无则默认线程池
 *      a. runAsync 无返回值
 *      b. supplyAsync 有返回值
 *
 *  2. 获得异步执行结果
 *      a. get() 阻塞获取执行结果;
 *      b. get(long timeout, TimeUnit unit) 带超时时间的阻塞获取异步执行结果
 *      c. getNow(T valueIfAbsent) 立即获得结果, 如果未执行完则返回传入的参数作为结果
 *      d. join() 不抛出异常的阻塞获取结果
 *      e. boolean complete(T value) 主动触发计算, 返回是否执行完成.
 *          若为执行没有完成,则返回true, get()得到结果为传入的参数
 *          若执行完成完成, 则返回false, get()得到异步任务的结果
 *
 *  3. 对执行结果进行处理
 *      a. whenComplete 链式调用, 等待前面任务执行完成后自动再执行当前处理
 *
 *  4. 对执行结果进行消费
 *      a. thenRun() 前面任务执行完成后执行当前任务, 不关心不依赖前面的任务, 也没有返回值
 *      b. thenAccept() 前面任务执行完成后执行当前任务, 消费前面任务的结果, 没有返回值
 *      c. thenApply() 前面任务执行完成后执行当前任务, 消费前面任务的结果, 有返回值
 *
 *  5. 异常处理
 *      a. exceptionally 异常捕获, 只消费前面任务出现的异常, 具有返回值, 捕获到异常则任务中后续的代码不执行, 类似 catch
 *
 *  6. 两组任务按顺序执行
 *      a. thenCompose 实现两组任务先后顺序执行, 前一组任务结果当做后一组任务的参数
 *
 *  7. 两组任务谁快用谁
 *      a. applyToEither 比较两组任务执行速度, 2组任务都会执行完成, 先执行完成的结果被返回通过get()获取
 *
 *  8. 两组任务完成后合并
 *      a. thenCombine 等两组任务执行完成后, 合并两组任务的执行结果. 第一个参数传入的是另一个执行任务, 第二个参数传入的是带2参数
 *          的函数式接口(接口中参数1是任务1的执行结果, 参数2是任务2的执行结果)
 *
 *  9. 多任务组合
 *      a. allOf() 实现并行的执行多个任务, 等待所有任务执行完成(无需任务间的考虑执行顺序)
 *      b. anyOf() 实现并行的执行多个任务, 只要有一个任务执行完成则返回该任务的执行结果
 *
 * @author CaiXiaowei
 * @date 2023/2/1
 */
public class CompletableFutureDemo {

    private static ExecutorService executorService = Executors.newFixedThreadPool(4);

    public static void main(String[] args) throws Exception {
        // 申明无返回值的 completableFuture
        CompletableFuture<Object> completableFuture = new CompletableFuture<>();
        // 申明有返回值的 completableFuture
        CompletableFuture<String> completableFutureWithResponse = CompletableFuture.completedFuture("");

        try {


//        // 1-a =>无返回
//        CompletableFuture.runAsync(() -> do1(), executorService);
//
//        // 1-b =>有返回
//        String do3Res = CompletableFuture.supplyAsync(() -> do3(), executorService).get();
//        System.out.println(do3Res);
//
//        // 2-e =>CompletableFuture.supplyAsync() 主动触发计算获得执行结果
//        CompletableFuture<String> supplyAsync = CompletableFuture.supplyAsync(() -> do4("44444"), executorService);
//        boolean complete = supplyAsync.complete("no completed");
//        System.out.println(complete);
//        System.out.println(supplyAsync.get());
//
//        // 3-a =>whenComplete
//        CompletableFuture.supplyAsync(() -> do4("444"), executorService)
//                .whenComplete((res, ex) -> doAfterComplete(res, ex));
//
//        // 4-a =>thenRun
//        CompletableFuture.supplyAsync(() -> do4("444"), executorService)
//                .thenRun(() -> System.out.println("thenRun--->"));
//
//        // 4-b =>thenAccept
//        CompletableFuture.supplyAsync(() -> do4("444"), executorService)
//                .thenAccept((res) -> System.out.println("thenAccept--->" + res));

            // 4-c =>thenApply
//            CompletableFuture.supplyAsync(() -> do4("444"), executorService)
//                    .thenApply((res) -> {
//                        return "thenApply: " + res;
//                    })
//                    .whenComplete((res, ex) -> doAfterComplete(res, ex));

            // 6-a =>先执行任务 do3(), 将do3() 返回值当做参数传给第二个任务
//            CompletableFuture.supplyAsync(() -> do3(), executorService)
//                    .thenCompose(res -> CompletableFuture.supplyAsync(() -> do4(res)))
//                    .whenComplete((res, ex) -> System.out.println(res));

            // 7-a =>先执行do4(), 后执行do5(), 最终返回的是先执行完成的do()响应
//            String finalResult = CompletableFuture.supplyAsync(() -> do5("555"), executorService)
//                    .applyToEither(CompletableFuture.supplyAsync(() -> do4("444"), executorService), (res) -> {
//                        System.out.println(res);
//                        return res;
//                    }).get();
//            System.out.println("最终结果: " + finalResult);

            // 8-a => 同时执行do4(), do6(), 等待两组任务都执行完, 合并执行结果
//            System.out.println("8-a ----------------->");
//            String finalResult8 = CompletableFuture.supplyAsync(() -> do4("444"), executorService)
//                    .thenCombine(CompletableFuture.supplyAsync(() -> do6("666")), (res1, res2) -> {
//                        System.out.println(res1);
//                        System.out.println(res2);
//                        return res1 + "&&" + res2;
//                    }).get();
//            System.out.println(finalResult8);

            // 9-a =>allOf() 并行执行多个任务, 使用get() 阻塞, 等待所有任务都执行完成
//            System.out.println("9-a ----------------->");
//            CompletableFuture<Void> allOf = CompletableFuture.allOf(
//                    CompletableFuture.supplyAsync(() -> do4("444"), executorService),
//                    CompletableFuture.supplyAsync(() -> do5("555"), executorService),
//                    CompletableFuture.supplyAsync(() -> do6("666"), executorService));
//            allOf.get();
//            System.out.println("9-a =>allOf()  都执行完成了!");

            // 9-b =>anyOf() 并行执行多个任务, do6()没有休眠, 最早执行完
            System.out.println("9-b ----------------->");
            CompletableFuture<Object> anyOf = CompletableFuture.anyOf(
                    CompletableFuture.supplyAsync(() -> do5("555"), executorService),
                    CompletableFuture.supplyAsync(() -> do6("666"), executorService),
                    CompletableFuture.supplyAsync(() -> do7("777"), executorService)
                    );
            Object o = anyOf.get();
            System.out.println("9-b =>anyOf()  都执行完成了!, 结果: " + o.toString());

        } catch (Exception e) {
            System.out.println(e);
        } finally {
            executorService.shutdown();
        }
    }

    private static void doAfterComplete(String res, Throwable ex) {
        System.out.println("执行结果: " + res + " and 异常: " + ex);
    }

    public static void do1() {
        System.out.println("do1--------------------->");
    }

    public static void do2(String var1) {
        System.out.println("do2--------------------->" + var1 + "--->");
    }

    public static String do3() {
        System.out.println("do3--------------------->");
        return "do3 执行完了";
    }


    public static String do4(String var1) {
        System.out.println("do4--------------------->" + var1 + "--->");
        return "do4 执行完了";
    }


    public static String do5(String var1) {
        try {
            Thread.sleep(20);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        System.out.println("do5--------------------->" + var1 + "--->");
        return "do5 执行完了";
    }


    public static String do6(String var1) {
        System.out.println("do6--------------------->" + var1 + "--->");
        return "do6 执行完了";
    }

    public static String do7(String var1) {
        try {
            Thread.sleep(50);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        System.out.println("do7--------------------->" + var1 + "--->");
        return "do7 执行完了";
    }
}
